//<%@ taglib prefix="jj" uri="http://jaxson.org/base" %>

//<jj:import>
org.jaxson.util.collections.Map;
org.jaxson.util.collections.List;
org.jaxson.util.Timer;
org.jaxson.util.http.XHR;
//</jj:import>

namespace( "org.jaxson.util.http" );

org.jaxson.util.http.ReverseAjaxBus = new function(){
	
	var lager;
	
	// Application specific configuration
	var url = ApplicationConfiguration.getProperty("client.reverse.ajax.url");
	var interval = ApplicationConfiguration.getProperty("client.reverse.ajax.poll.interval")*1000;
	
	var observer = function( name, value ){
		
		if( name === "client.reverse.ajax.url" ){
			
			if( xhr.isChunked() )
				return;
			
			timer.stop();
			url = value;
			timer.start();
		}
		
		if( name === "client.reverse.ajax.poll.interval" ){
			
			if( xhr.isChunked() )
				return;
			
			interval = value*1000;
			timer.stop();
			timer = new org.jaxson.util.Timer(doRequest, interval);
			timer.start();
		}
	};
	
	ApplicationConfiguration.addObserver( "client.reverse.ajax.url", observer );
	ApplicationConfiguration.addObserver( "client.reverse.ajax.poll.interval", observer );
	
	if( !interval )
		interval = 15000;
	
	var xhr = new org.jaxson.util.http.XHR();
	var subscribers = new org.jaxson.util.collections.Map();
	var initialized = false;
	var retries = 0;
	var totalRetries = parseInt( ApplicationConfiguration.getProperty("client.reverse.ajax.error.reattempts") );
	xhr.setTimeout( interval - 100 );
	xhr.onerror = function(e){
		retries++;
		
		var evt = {
			name: "reverse.ajax.connection.error",
			attemptCount: retries,
			rawError : e
		};
		
		notify( {events:[evt]} );
		
		if( retries >= totalRetries ){
			var evt2 = {
				name: "reverse.ajax.connection.error.limit",
				rawError : e,
				totalRetries : totalRetries
			};
			
			notify( {events:[evt2]} );
		}
	};
	
	var timer = new org.jaxson.util.Timer(
		function(){
		
			if( !xhr.isChunked() /*&& initialized === false*/ ){
				xhr.setMethod( "POST" );
				xhr.doRequest( url );
			}
	
			if( !initialized ){
				initialized = true;
			}
		}, interval);
	
	function notify( result ){
		
		lager.debug( "notify called" );
		lager.dump( result );
		
		// for each event...
		for( i=0; i<result.events.length; i++ )
		{
			var evt = result.events[i];
			
			// ... look for a matching list of subscribers
			// and if one exists....
			if( subscribers.get(evt.name) != null )
			{
				var list = subscribers.get( evt.name );
				
				// ... fire the event for each one
				for( j=0; j<list.size(); j++ ){
					list.get(j)(evt);
				}
			}
		}
	}
	
	var chunkingInitialized = false;
	var responseHandler = function(){
		
		lager.trace( "responseHandler running" );
		
		var xhrptr = this;
		// for a chunked (streaming) request
		// overwrite the timer.stop function
		// so that calling "stop" on an instance
		// of this object will abort the request
		if( xhrptr.isChunked() && !chunkingInitialized ){
			
			lager.debug( "handling streaming reverse ajax");
			
			chunkingInitialized = true;
			timer.stop();
			newstop = function(){
				xhrptr.abort();
			};
			timer.stop = newstop;
		}
		
		// clear the error state from any subscribers
		if( retries > 0 && !xhrptr.inErrorState() ){
			var evt = {
				name: "reverse.ajax.connection.error.cleared"
			};
			
			retries = 0;
			notify( {events:[evt]} );
		}
		
		// fire the event
		var response = this.getResponseText();
		
		if( response === undefined || response === null || response.trim().length === 0 ){
			return;
		}
		
		eval( "result=" + response );
		
		if( result === null || result === undefined || result.events === undefined )
			return;
		
		notify( result );
	};
	
	//xhr.oncomplete = chunkhandler;
	//xhr.onrecieved = responseHandler;
	xhr.oncomplete = responseHandler;
	
	var that = {
		subscribe : function (evtname, func ){
			
			if( subscribers.get(evtname) === null )
			{
				subscribers.put( evtname, new org.jaxson.util.collections.List() );
			}
			
			var list = subscribers.get( evtname );
			list.add( func );
		},
		
		unsubscribe : function( evtname, func ){
			if( subscribers.get(evtname) === null )
				return;
			
			var list = subscribers.get( evtname );
			list.remove( func );
		},
		
		setUrl : function( purl ){
			url = purl;
		},
		
		start : function(){
			
			if( url === null )
				throw "url not set.";
			
			timer.start();
		},
		
		stop : function(){
			timer.stop();
		},
		
		setLogger: function( logger ){
			lager = logger;
			xhr.setLogger( new Logger( "org.jaxson.util.http.XHR::ReverseAjaxBus", Logger.LEVEL_INFO ) );
		}
	};
	
	// since ApplicationConfiguration relies on reverse ajax
	// for configuration events pushed from the server, 
	// initialize it when this class has loaded.
	ApplicationConfiguration.initialize(that);
	return that;
}

ReverseAjaxBus = org.jaxson.util.http.ReverseAjaxBus;
